﻿using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using PropertyModels.ComponentModel;
using PropertyModels.Extensions;

namespace Avalonia.PropertyGrid.Controls.Implements
{
	/// <summary>
	/// Class CellEditFactoryCollection.
	/// Implements the <see cref="Avalonia.PropertyGrid.Controls.ICellEditFactoryCollection" />
	/// </summary>
	/// <seealso cref="Avalonia.PropertyGrid.Controls.ICellEditFactoryCollection" />
	internal class CellEditFactoryCollection : ICellEditFactoryCollection
	{
		/// <summary>
		/// The factories
		/// </summary>
		private readonly List<ICellEditFactory> _factories = [];

		/// <summary>
		/// Gets the factories.
		/// </summary>
		/// <value>The factories.</value>
		public IEnumerable<ICellEditFactory> Factories => [.. _factories];

		/// <summary>
		/// Initializes a new instance of the <see cref="CellEditFactoryCollection"/> class.
		/// </summary>
		public CellEditFactoryCollection()
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="CellEditFactoryCollection"/> class.
		/// </summary>
		/// <param name="factories">The factories.</param>
		public CellEditFactoryCollection(IEnumerable<ICellEditFactory> factories)
		{
			_factories.AddRange(factories);
			_factories.Sort((x, y) => Comparer<int>.Default.Compare(y.ImportPriority, x.ImportPriority));

			foreach (var factory in _factories)
			{
				factory.Collection = this;
			}
		}

		/// <summary>
		/// Clones the factories.
		/// </summary>
		/// <param name="accessToken">The access token.</param>
		/// <returns>IEnumerable&lt;ICellEditFactory&gt;.</returns>
		public IEnumerable<ICellEditFactory> CloneFactories(object accessToken)
			=> _factories
			.FindAll(x => x.Accept(accessToken))
			.Select(x => x.Clone())
			.Where(x => x != null)
			.Select(x => x!);

		/// <summary>
		/// Adds the factory.
		/// </summary>
		/// <param name="factory">The factory.</param>
		public void AddFactory(ICellEditFactory factory)
		{
			factory.Collection = this;
			_factories.Add(factory);
			_factories.Sort((x, y) => Comparer<int>.Default.Compare(y.ImportPriority, x.ImportPriority));
		}

		/// <summary>
		/// Removes the factory.
		/// </summary>
		/// <param name="factory">The factory.</param>
		public void RemoveFactory(ICellEditFactory factory) => _ = _factories.Remove(factory);

		/// <summary>
		/// Builds the property control.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <returns>Control.</returns>
		public Control? BuildPropertyControl(PropertyCellContext context)
		{
			foreach (var factory in _factories)
			{
				var control = factory.HandleNewProperty(context);

				if (control != null)
				{
					var classesAttributes = context.Property.GetCustomAttributes<ControlClassesAttribute>();
					if (classesAttributes.Length > 0)
					{
						var classes = classesAttributes.SelectMany(a => a.Classes).Distinct();
						control.Classes.AddRange(classes);
					}

					context.CellEdit = control;
					context.Factory = factory;

					return control;
				}
			}

			return null;
		}
	}
}
